/*
 * @Author: yehuozhili
 * @Date: 2021-11-30 21:53:42
 * @LastEditors: yehuozhili
 * @LastEditTime: 2021-12-02 00:24:08
 * @FilePath: \learnts\src\start.tsx
 */
export {};
// type CapitalizeString<T> = T extends `${infer R}${infer K}` ? `${Uppercase<R>}${K}` : T
// type CapitalizeString<T extends string | number> = T extends string? Capitalize<T> : T;
// type a1 = CapitalizeString<'handler'> // Handler
// type a2 = CapitalizeString<'parent'> // Parent
// type a3 = CapitalizeString<233> // 233

// type FirstChar<T> = T extends `${infer R}${infer S}` ? R : never
// type A = FirstChar<'BFE'> // 'B'
// type B = FirstChar<'dev'> // 'd'
// type C = FirstChar<''> // never

// type LastChar<T extends string, A extends string[] = []> = T extends `${infer P}${infer Q}`
//   ? LastChar<Q, [...A, P]>
//   : A extends [...infer L, infer R]
//   ? R
//   : never
// type A = LastChar<'BFE'> // 'E'
// type B = LastChar<'dev'> // 'v'
// type C = LastChar<'D'> // never

// type StringToTuple<T, A extends string[] = []> =T extends `${infer P}${infer Q}`
//   ? StringToTuple<Q, [...A, P]>
//   :  A
// type A = StringToTuple<'BFE.dev'> // ['B', 'F', 'E', '.', 'd', 'e','v']
// type B = StringToTuple<''> // []

// type TupleToString<T, R extends string = ''> = T extends [infer A, ...infer B]
//   ? A extends `${infer AA}`
//     ? TupleToString<B, `${R}${AA}`>
//     : ''
//   : R

// type A = TupleToString<['a', 'b', 'c']> // 'abc'
// type B = TupleToString<[]> // ''
// type C = TupleToString<['a']> // 'a'

// 利用数组length记录递归次数
// type RepeatString<
//   T extends string,
//   C extends number,
//   S extends string = '',
//   A extends any[] = []
// > = A['length'] extends C ? S : RepeatString<T, C, `${T}${S}`, [1, ...A]>
// type A = RepeatString<'a', 3> // 'aaa'
// type B = RepeatString<'a', 0> // ''

// type SplitString<
// 	T,
// 	Separator extends string,
// 	A extends any[] = []
// > = T extends ""
// 	? A
// 	: T extends `${infer L}${Separator}${infer R}`
// 	? SplitString<R, Separator, [...A, L]>
// 	: [...A, T];

// type A1 = SplitString<"handle-open-flag", "-">; // ["handle", "open", "flag"]
// type A2 = SplitString<"open-flag", "-">; // ["open", "flag"]
// type A3 = SplitString<"handle.open.flag", ".">; // ["handle", "open", "flag"]
// type A4 = SplitString<"open.flag", ".">; // ["open", "flag"]
// type A5 = SplitString<"open.flag", "-">; // ["open.flag"]

// type LengthOfString<T extends string, TT extends any[] = []> = T extends ""
// 	? TT["length"]
// 	: T extends `${infer L}${infer R}`
// 	? LengthOfString<R, [...TT, L]>
// 	: never;
// type A = LengthOfString<"BFE.dev">; // 7
// type B = LengthOfString<"">; // 0

// type KebabCaseIterator<T extends string, TT extends any[] = []> = T extends ""
// 	? TT
// 	: T extends `${infer L}${infer R}`
// 	? KebabCaseIterator<
// 			R,
// 			[...TT, L extends Uppercase<L> ? `-${Lowercase<L>}` : L]
// 	  >
// 	: never;
// type JoinString<T> = T extends [infer L, ...infer R]
// 	? L extends string
// 		? `${L}${JoinString<R>}`
// 		: never
// 	: "";
// type RemoveFstSeparator<T> = T extends `-${infer R}` ? R : T;
// type KebabCase<T extends string> = RemoveFstSeparator<
// 	JoinString<KebabCaseIterator<T>>
// >;
// type a1 = KebabCase<"HandleOpenFlag">; // handle-open-flag
// type a2 = KebabCase<"OpenFlag">; // open-flag

// type SplitString<
// 	T,
// 	Separator extends string,
// 	A extends any[] = []
// > = T extends ""
// 	? A
// 	: T extends `${infer L}${Separator}${infer R}`
// 	? SplitString<R, Separator, [...A, L]>
// 	: [...A, T];
// type CamelCaseIterator<T, Ret extends string = ""> = T extends [
// 	infer L,
// 	...infer R
// ]
// 	? L extends string
// 		? CamelCaseIterator<R, `${Ret}${Capitalize<L>}`>
// 		: never
// 	: Ret;
// type CamelCase<T> = CamelCaseIterator<SplitString<T, "-">>;
// type a1 = CamelCase<"handle-open-flag">; // HandleOpenFlag
// type a2 = CamelCase<"open-flag">; // OpenFlag

// type JoinString<
// 	T extends string,
// 	K extends string,
// 	Separator extends string
// > = T extends ""
// 	? K extends ""
// 		? never
// 		: K
// 	: K extends ""
// 	? T
// 	: `${T}${Separator}${K}`;
// type js1 = JoinString<"top", "title", ".">; // top.title
// type js2 = JoinString<"", "title", ".">; // title
// type js3 = JoinString<"top", "", ".">; // top
// type js4 = JoinString<"", "", ".">; // never
// type ObjectAccessPaths<
// 	T extends Record<string, any>,
// 	Prev extends string = "",
// 	K = keyof T
// > = K extends keyof T
// 	? K extends string
// 		? T[K] extends Record<string, any>
// 			? ObjectAccessPaths<T[K], JoinString<Prev, K, ".">>
// 			: JoinString<Prev, K, ".">
// 		: never
// 	: never;
// // 简单来说，就是根据如下对象类型：
// /*
// {
//     home: {
//         topBar: {
//             title: '顶部标题',
//             welcome: '欢迎登录'
//         },
//         bottomBar: {
//             notes: 'XXX备案，归XXX所有',
//         },
//     },
//     login: {
//         username: '用户名',
//         password: '密码'
//     }
// }
// */
// // 得到联合类型：
// /*
// home.topBar.title | home.topBar.welcome | home.bottomBar.notes | login.username | login.password
// */
// // 完成 createI18n 函数中的 ObjectAccessPaths<Schema>，限制函数i18n的参数为合法的属性访问字符串
// function createI18n<Schema>(
// 	schema: Schema
// ): (path: ObjectAccessPaths<Schema>) =>  string {
// 	return [{ schema }] as any;
// }
// // i18n函数的参数类型为：home.topBar.title | home.topBar.welcome | home.bottomBar.notes | login.username | login.password
// const i18n = createI18n({
// 	home: {
// 		topBar: {
// 			title: "顶部标题",
// 			welcome: "欢迎登录",
// 		},
// 		bottomBar: {
// 			notes: "XXX备案，归XXX所有",
// 		},
// 	},
// 	login: {
// 		username: "用户名",
// 		password: "密码",
// 	},
// });

// i18n("home.topBar.title"); // correct
// i18n("home.topBar.welcome"); // correct
// i18n("home.bottomBar.notes"); // correct

// // i18n('home.login.abc')              // error，不存在的属性
// // i18n('home.topBar')                 // error，没有到最后一个属性

// 实现 ComponentEmitsType<Emits> 类型，将
/*
{
    'handle-open': (flag: boolean) => true,
    'preview-item': (data: { item: any, index: number }) => true,
    'close-item': (data: { item: any, index: number }) => true,
}
*/
// 转化为类型
/*
{
    onHandleOpen?: (flag: boolean) => void,
    onPreviewItem?: (data: { item: any, index: number }) => void,
    onCloseItem?: (data: { item: any, index: number }) => void,
}
*/

type SplitString<
	T,
	Separator extends string,
	A extends any[] = []
> = T extends ""
	? A
	: T extends `${infer L}${Separator}${infer R}`
	? SplitString<R, Separator, [...A, L]>
	: [...A, T];
type CamelCaseIterator<T, Ret extends string = ""> = T extends [
	infer L,
	...infer R
]
	? L extends string
		? CamelCaseIterator<R, `${Ret}${Capitalize<L>}`>
		: never
	: Ret;
type CamelCase<T> = CamelCaseIterator<SplitString<T, "-">>;
type GetListenName<T> = `on${CamelCase<T>}`;
type RemoveReturn<T> = T extends (...args: infer A) => any
	? (...args: A) => void
	: T;
type Convert<T> = { [k in keyof T as GetListenName<k>]: RemoveReturn<T[k]> };
type ComponentEmitsType<Emits> = { (props: Partial<Convert<Emits>>): any };

function createComponent<Emits extends Record<string, any>>(
	emits: Emits
): ComponentEmitsType<Emits> {
	return [{ emits }] as any;
}

// 最后返回的 Component变量类型为一个合法的React组件类型，并且能够通过`on事件驼峰命名`的方式，监听定义的事件，并且能够自动推导出事件的参数类型
const Component = createComponent({
	"handle-open": (flag: boolean) => true,
	"preview-item": (data: { item: any; index: number }) => true,
	"close-item": (data: { item: any; index: number }) => true,
});
console.log(
	<Component
		// onHandleOpen 的类型为 (flag: boolean) => void
		onHandleOpen={(val) => console.log(val.valueOf())}
		// onPreviewItem 的类型为 (data: { item: any, index: number }) => void
		onPreviewItem={(val) => {
			const { item, index } = val;
			const a: number = item;
			console.log(a, index.toFixed(2));
		}}
		// 所有的监听事件属性都是可选属性，可以不传处理函数句柄
		// onCloseItem={val => [{val}]}
	/>
);

// 提示，定义组件的props类型方式为 { (props: Partial<Convert<Emits>>): any }
// 比如 Comp 可以接收属性 {name:string, age:number, flag:boolean, id?:string}，其中id为可选属性，那么可以这样写

const Comp: {
	(props: { name: string; age: number; flag: boolean; id?: string }): any;
} = Function as any;

console.log(<Comp name="" age={1} flag />); // 正确
console.log(<Comp name="" age={1} flag id="111" />); // 正确
// console.log(<Comp name={1} age={1} flag/>)          // 错误，name为字符串类型
// console.log(<Comp age={1} flag/>)                   // 错误，缺少必须属性name:string
